/****************************************************
          File Name:http_response.h
          Author: Abby Cin
          Mail: abbytsing@gmail.com
          Created Time: Thu 05 Mar 2015 04:45:34 PM CST
*****************************************************/

#ifndef http_response_h_
#define http_response_h_

#include "header.h"
#include "global.h"
#include <string.h>
#include <time.h>
#include <stdint.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <ctype.h>

#define CRLF "\r\n"

extern int pisces_log(int, const char *, ...);

typedef struct status {
    int code;
    char *value;
} abby_status;

void transfer_data(int);

/* only implemented a few responses */
abby_status Status[40] = {
    /* success */
    {200, "200 OK"},
    {206, "206 Partial Content"},
    /* redirect */
    {304, "304 Not Modified"},
    /* error */
    {403, "403 Forbidden"},
    {404, "404 Not Found"},
    /* server error */
    {503, "503 Service Unavailable"},
};

static void get_date_expire(char *gm_date, char *gm_expire, size_t max)
{
    time_t tmp;
    struct tm *gm;

    tmp = time(NULL);
    gm = (struct tm *)malloc(sizeof(struct tm));
    gmtime_r(&tmp, gm);
    strftime(gm_date, max, "%a, %d %b %Y %H:%M:%S GMT", gm);

    memset(gm, 0, sizeof(struct tm));

    if (gm_expire) {
        tmp += 2592000;         /* expire in a month */
        gmtime_r(&tmp, gm);
        strftime(gm_expire, max, "%a, %d %b %Y %H:%M:%S GMT", gm);
    }
    free(gm);
}

static int makeEtag(char *etag, time_t mtime)
{
    char *chouchou = "eab980ed839cec97b0";
    int limit = 238;            /* 256 - strlen(chouchou) */
    int i = 0, alpha;
    mtime -= 19890309;
    if (mtime < 0)
        mtime = -mtime;

    strncpy(etag, chouchou, 18);
    for (; (mtime != 0) && i < limit;) {
        alpha = mtime % 16;
        mtime /= 16;
        alpha > 9 ? (alpha += 87) : (alpha += '0');
        etag[18 + i] = alpha;
        i++;
    }
    return 18 + i;
}

static char *n2s(char *res, intmax_t number)
{
    intmax_t keep = number;
    int len = 0;
    for (; number;) {
        number /= 10;
        len++;
    }
    res = (char *)malloc(len);
    snprintf(res, len + 1, "%jd", (intmax_t) keep);
    return res;
}

static void concat(char *target, const char *key, const char *value)
{
    if (value) {
        strcat(target, key);
        strcat(target, value);
        strcat(target, CRLF);
    } else {
        strcat(target, key);
        strcat(target, CRLF);
    }
}

void http_response(abby_response resp)
{
    int n = 0;
    char header[CONTENT_SIZE];
    char *date = NULL;
    char *expire = NULL;
    char *res = NULL;           /* hold converted string */
    char etag[256];

    date = (char *)malloc(30);
    expire = (char *)malloc(30);

    for (; Status[n].code != resp.code;)
        n++;
    /* HTTP/1.x xxx */
    memset(header, 0, CONTENT_SIZE);
    concat(header, "HTTP/1.1 ", Status[n].value);
    /* Server name */
    concat(header, "Abby's HTTP Server", NULL);
    /* Date */
    get_date_expire(date, expire, 30);  /* GMT date length 30 */
    concat(header, "Date: ", date);
    free(date);
    /* Etag */
    int size;
    size = makeEtag(etag, resp.mtime);
    etag[size] = '\0';
    concat(header, "Etag: ", etag);
    /* Content-Type */
    concat(header, "Content-type: ", resp.type);
    /* Content-Length */
    off_t len;
    len =
        resp.range.offset > 0 ? (resp.range.end >
                                 0 ? resp.range.end - resp.range.offset + 1 : resp.size) : resp.size;
    res = n2s(res, len);
    concat(header, "Content-length: ", res);
    free(res);
    res = NULL;
    /* Connection */
    concat(header, "Connection: keep-alive", NULL);
    /* Last-Modified */
    if (resp.mtime != 0) {
        res = (char *)malloc(30);
        struct tm *gm = (struct tm *)malloc(sizeof(struct tm));
        gm = localtime_r(&resp.mtime, gm);
        strftime(res, 30, "%a, %d %b %Y %H:%M:%S GMT", gm);
        concat(header, "Last-Modified: ", res);
        free(gm);
        free(res);
        res = NULL;
    } else
        concat(header, "Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT", NULL);
    /* Expires */
    concat(header, "Expires: ", expire);
    free(expire);
    /* Cache-Control */
    concat(header, "Cache-Control: ", "max-age=2592000");
    /* Ranges */
    concat(header, "Accept-Ranges: bytes", NULL);

    /* Content-Range */
    if (Status[n].code == 206) {
        off_t end;
        resp.range.offset == 0 || resp.range.end < 1 ? (end = resp.size - 1) : (end = resp.range.end);
        sprintf(header, "%sContent-Range: bytes %jd-%jd/%jd\r\n", header,
                (intmax_t) resp.range.offset, (intmax_t) end, (intmax_t) resp.size);
    }
    /* append CRLF */
    strcat(header, "\r\n");

    len = strlen(header);
    write(resp.confd, header, len);
}

#endif
